package com.icw.utility;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.persistence.CascadeType;
import javax.persistence.FetchType;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.proxy.HibernateProxy;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.icw.basis.BasePoEntity;
import com.icw.basis.DiyMap;
import com.icw.basis.MyRunnable;
import com.icw.basis.PropertySettings;
import com.icw.configuration.ConstSettings;
import com.icw.configuration.poentity.PoVali;
import com.icw.configuration.poentity.vo.PoMode;
import com.icw.configuration.poentity.vo.PropertiesData;
import com.icw.configuration.poentity.vo.PropertyDescription;
import com.icw.configuration.poentity.vo.PropertyGroup;
import com.icw.decoupling.defi.proxy.GenericSrvProxy;
import com.icw.decoupling.defi.tool.DataExchange;
import com.icw.decoupling.defi.tool.DictManager;
import com.icw.decoupling.defi.tool.ResourceContextProcessor;
import com.icw.resource.inner.entity.IcwPerm;
import com.icw.utility.inflector.Inflector;
import com.icw.utility.query.IcwCriteria;
import com.icw.utility.query.IcwCriterion;
import com.icw.utility.query.IcwProjection;

public final class PoEntityHelper {

	private static Set<String> GROUP_NAME_SET = null;

	private static final Set<Class<?>> entities = new TreeSet<>((o1, o2) -> String.CASE_INSENSITIVE_ORDER.compare(o1.getSimpleName(), o2.getSimpleName()));

	private static final Set<String> commomFieldNames = new HashSet<String>(Arrays.asList(new String[] { "class", "id", "createTime" }));

	private static final String NUMBER_REGEX = "([1-9][0-9]+)|[0-9]";

	private static final String ARRY_RPLC_MARK = "_ARRY_MARK";

	private PoEntityHelper() {
	}

	public static Set<Class<?>> getTreeSetForClass() {
		if (entities.size() == 0) {
			try {
				entities.addAll(IcwUtils.getAllEntities());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return entities;
	}

	public static String fieldUnderScore(PropertyDescriptor pd) {
		Class<?> entityClass = pd.getReadMethod().getDeclaringClass();
		String tableName = IcwUtils.toUnderScoreCase(entityClass.getSimpleName());
		String identifyTableName = tableName.substring(tableName.indexOf("_") + 1).toUpperCase();
		String key = identifyTableName + "_" + IcwUtils.toUnderScoreCase(pd.getName()).toUpperCase();
		return key;
	}

	public static boolean isTxea(PropertyDescriptor pd, Class<?> fieldClazz) {
		if (fieldClazz != String.class) {
			return false;
		} else if (isMult(pd.getPropertyType())) {
			return false;
		} else {
			try {
				Field field = pd.getReadMethod().getDeclaringClass().getDeclaredField(pd.getName());
				return Stream.of(field.getDeclaredAnnotations()).anyMatch(a -> {
					Method m;
					try {
						m = a.getClass().getMethod("columnDefinition");
					} catch (NoSuchMethodException e1) {
						return false;
					} catch (SecurityException e1) {
						return false;
					}
					try {
						String def = (String) m.invoke(a);
						return BasePoEntity.TEXT_COLUMN.equals(def);
					} catch (Exception e) {
						return false;
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		}
	}

	public static boolean isOref(Class<?> fieldClazz) {
		return getTreeSetForClass().contains(fieldClazz);
	}

	public static boolean isText(Class<?> fieldClazz) {
		return fieldClazz == String.class;
	}

	public static boolean isNmbr(Class<?> fieldClazz) {
		return Number.class.isAssignableFrom(fieldClazz) || (fieldClazz.getSuperclass() == null && fieldClazz != Object.class);
	}

	public static boolean isDate(Class<?> fieldClazz) {
		return Date.class.isAssignableFrom(fieldClazz);
	}

	public static boolean isBool(Class<?> fieldClazz) {
		return fieldClazz == boolean.class || fieldClazz == Boolean.class;
	}

	public static boolean isPswd(PropertyDescriptor pd, Class<?> fieldClazz) {
		return fieldClazz == String.class && pd.getName().toLowerCase().indexOf("password") >= 0;
	}

	public static boolean isDict(PropertyDescriptor pd, Class<?> fieldClazz) throws Exception {
		return findDGN(pd, fieldClazz) != null;
	}

	public static boolean isArry(Class<?> fieldClazz, String property) {
		PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(fieldClazz, property);
		return Collection.class.isAssignableFrom(pd.getReadMethod().getReturnType());
	}

	public static Boolean isSingleNameOref(Class<?> fieldClazz) {
		PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(fieldClazz);
		Set<PropertyDescriptor> set = Stream.of(pds)// e
				.filter(propd -> !commomFieldNames.contains(propd.getName()))// e
				.filter(propd -> !getTreeSetForClass().contains(propd.getReadMethod().getReturnType()))// e
				.filter(propd -> !isMult(propd.getReadMethod().getReturnType()))// e
				.collect(Collectors.toSet());
		if (set.size() == 1 && set.stream().anyMatch(s -> s.getName().equals("name"))) {
			return true;
		} else {
			return false;
		}
	}

	public static Class<?> findComponentType(PropertyDescriptor pd) {
		if (pd.getPropertyType().isArray()) {
			return pd.getPropertyType().getComponentType();
		} else {
			return (Class<?>) ((ParameterizedType) pd.getReadMethod().getGenericReturnType()).getActualTypeArguments()[0];
		}
	}

	public static String findDGN(PropertyDescriptor pd, Class<?> fieldClazz) throws Exception {
		if (fieldClazz != String.class) {
			return null;
		}
		String fu;
		String tfu;
		if (findDictGroupNameSet().contains(tfu = fieldUnderScore(pd))) {
			return tfu;
		} else if (findDictGroupNameSet().contains(fu = IcwUtils.toUnderScoreCase(pd.getName()).toUpperCase())) {
			return fu;
		} else {
			return null;
		}
	}

	public static String findDGN(Class<? extends BasePoEntity> clazz, String propertyName) throws Exception {
		PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, propertyName);
		String fu;
		String tfu;
		if (findDictGroupNameSet().contains(tfu = fieldUnderScore(pd))) {
			return tfu;
		} else if (findDictGroupNameSet().contains(fu = IcwUtils.toUnderScoreCase(pd.getName()).toUpperCase())) {
			return fu;
		} else {
			return null;
		}
	}

	public static Set<String> findDictGroupNameSet() throws Exception {
		if (GROUP_NAME_SET == null) {
			synchronized ("GROUP_NAME_SET") {
				if (GROUP_NAME_SET == null) {
					GROUP_NAME_SET = SetUtils.unmodifiableSet(BeanCtxUtils.getBeanByType(DictManager.class).dictGroupNames());
				}
			}
		}
		return GROUP_NAME_SET;
	}

	public static boolean filterPd(PropertyDescriptor p) {
		return true// e
				&& p.getPropertyType() != Class.class // e
				&& !(p.getName().indexOf("Str") == p.getName().length() - 3 && p.getName().indexOf("Str") > 0);
	}

	public static boolean isMult(Class<?> fieldClazz) {
		return fieldClazz.isArray() || Collection.class.isAssignableFrom(fieldClazz);
	}

	public static Boolean isNullable(Class<?> declaringClazz, String fieldName) {
		return AnnoHelper.isAnnoable(declaringClazz, fieldName, "nullable");
	}

	public static Boolean isInsertable(Class<?> declaringClazz, String fieldName) {
		return AnnoHelper.isAnnoable(declaringClazz, fieldName, "insertable");
	}

	public static Boolean isUpdatable(Class<?> declaringClazz, String fieldName) {
		return AnnoHelper.isAnnoable(declaringClazz, fieldName, "updatable");
	}

	public static Boolean isCascadable(Class<?> declaringClazz, String fieldName) {
		CascadeType[] types = AnnoHelper.findAnnoValue(declaringClazz, fieldName, "cascade", CascadeType[].class);
		if (types == null) {
			return false;
		} else {
			return Stream.of(types).anyMatch(t -> t == CascadeType.REMOVE);
		}
	}

	public static String toLabel(PropertyDescription field) {
		String[] words = IcwUtils.toUnderScoreCase(field.getName()).split("_");
		List<String> list = Stream.of(words)// e
				.map(w -> IcwUtils.toFirstUpperCase(w))// e
				.collect(Collectors.toList());
		return StringUtils.join(list, ' ');
	}

	public static void orderFields(List<PropertyDescription> fields, Class<?> temp) {
		Map<String, Integer> field_index_map = new LinkedHashMap<>();
		Field[] fieldArray = temp.getDeclaredFields();
		for (int i = 0; i < fieldArray.length; i++) {
			field_index_map.put(fieldArray[i].getName(), i);
		}
		Map<Integer, PropertyDescription> index_field_map = new TreeMap<>();
		int index_count = 0;
		for (PropertyDescription field : fields) {
			Integer index = field_index_map.get(field.getName());
			if (index == null) {
				index = 999 + (index_count++);
			}
			index_field_map.put(index, field);
		}
		fields.clear();
		fields.addAll(index_field_map.values());
	}

	public static boolean isLazy(Class<? extends Object> class1, String name) {
		try {
			while (class1.getSuperclass() != BasePoEntity.class && class1 != Object.class) {
				class1 = class1.getSuperclass();
			}
			return FetchType.LAZY == AnnoHelper.findAnnoValue(class1, name, "fetch", FetchType.class);
		} catch (Exception e) {
			return false;
		}
	}

	public static PoVali createValiByFieldDeclaring(Class<? extends BasePoEntity> clazz, String fieldName) {
		PoVali poVali = new PoVali();
		// 1. nullable
		if (!isNullable(clazz, fieldName)) {
			poVali.notNull();
		}
		// 2. length
		if (findTypeByProperty(clazz, fieldName) == String.class) {
			Integer length = AnnoHelper.findAnnoValue(clazz, fieldName, "length", Integer.class);
			if (length != null) {
				String columnDefinition = AnnoHelper.findAnnoValue(clazz, fieldName, "columnDefinition", String.class);
				if (!BasePoEntity.TEXT_COLUMN.equals(columnDefinition)) {
					poVali.maxLen(length);
				}
			}
		}
		Set<String> wordSet = Stream.of(IcwUtils.toUnderScoreCase(fieldName).split("_")).collect(Collectors.toSet());
		// 3. pswd
		if (wordSet.contains("password")) {
			poVali.password();
		}
		// 4. unique
		Boolean unique = AnnoHelper.findAnnoValue(clazz, fieldName, "unique", Boolean.class);
		if (IcwUtils.ifNull(unique, () -> Boolean.FALSE).booleanValue() == Boolean.TRUE.booleanValue()) {
			poVali.unique();
		}
		// 5. cron expression
		if (wordSet.contains("cron")) {
			poVali.cron();
		}
		return poVali;
	}

	public static boolean containsKeyByProperty(Object root, String propertyPath) {
		String[] properties = splitProperties(propertyPath);
		String[] parentProperties = ArrayUtils.subarray(properties, 0, properties.length - 1);
		String parentPropertyPath = StringUtils.join(parentProperties, ",");
		Object parent = readValueByProperty(root, parentPropertyPath);
		String property = properties[properties.length - 1];
		if (parent == null) {
			return false;
		} else if (Collection.class.isInstance(parent) || parent.getClass().isArray()) {
			return Integer.valueOf(property, 10) < toObjectArray(parent).length;
		} else if (Map.class.isInstance(parent)) {
			return Map.class.cast(parent).containsKey(property);
		} else {
			return BeanUtils.getPropertyDescriptor(parent.getClass(), property) != null || IcwUtils.tryCatch(p -> p.getClass().getField(property), parent) != null;
		}
	}

	public static Object readValueByProperty(Object root, String propertyPath) {
		if (Map.class.isInstance(root) && Map.class.cast(root).containsKey(propertyPath)) {
			return Map.class.cast(root).get(propertyPath);
		}
		String[] propertySplits = splitProperties(propertyPath);
		Object object = root;
		for (int i = 0; i < propertySplits.length; i++) {
			String property = propertySplits[i];
			if (null == object) {
				break;
			} else if (Collection.class.isInstance(object) || object.getClass().isArray()) {
				Object[] objects = toObjectArray(object);
				if (Pattern.matches(NUMBER_REGEX, property)) {
					Integer index = Integer.valueOf(property, 10);
					object = index >= objects.length ? null : objects[index];
				} else {
					for (int xx = 0; xx < objects.length; xx++) {
						objects[xx] = PoEntityHelper.readValueByProperty(objects[xx], property);
					}
					object = objects;
				}
			} else if (Map.class.isInstance(object)) {
				object = ((Map<?, ?>) object).get(property);
			} else {
				PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(object.getClass(), property);
				if (pd == null) {
					object = IcwUtils.tryCatch(o -> o.getClass().getField(property).get(o), object);
				} else {
					Object oo = object;
					object = IcwUtils.tryCatch(() -> pd.getReadMethod().invoke(oo));
					if (true// e
							&& isLazyInstance(object) // e
							&& i != propertySplits.length - 1 // e
							&& !(i == propertySplits.length - 2 && "id".equals(propertySplits[i + 1]))// e
					) {
						object = loadLazyInstance(object);
					}
				}
			}
		}
		return object;
	}

	public static Object readDeclaredByProperty(Object root, String propertyPath) throws Exception {
		String[] properties = splitProperties(propertyPath);
		Object obj = root;
		for (String property : properties) {
			if (obj == null) {
				break;
			} else {
				Field field = obj.getClass().getDeclaredField(property);
				field.setAccessible(true);
				obj = field.get(obj);
			}
		}
		return obj;
	}

	@SuppressWarnings("unchecked")
	public static void writeValueByProperty(Object root, String propertyPath, Object value) {
		if (root == null) {
			return;
		} else {
			String[] propertySplits = splitProperties(propertyPath);
			Object object = root;
			Object parent = null;
			for (int i = 0; i < propertySplits.length - 1; i++) {
				String property = propertySplits[i];
				Object nextObject = null;
				if (object.getClass().isArray()) {
					List<Object> list = Stream.of(toObjectArray(object)).collect(Collectors.toList());
					writeValueByProperty(parent, propertySplits[i - 1], object = list);
				}
				if (Collection.class.isInstance(object)) {
					Object[] tempCollection = toObjectArray(object);
					int length = tempCollection.length;
					int index = isNumberAppendChar(property) ? length : Integer.valueOf(property, 10);
					int finalLength = Math.max(length, index + 1);
					if (index < length) {
						nextObject = tempCollection[index];
					}
					if (nextObject == null) {
						nextObject = isNumberProperty(propertySplits[i + 1]) ? IcwUtils.listObj() : IcwUtils.mapObject();
						Collection.class.cast(object).clear();
						for (int obj_i = 0; obj_i < finalLength; obj_i++) {
							if (Integer.compare(obj_i, index) == 0) {
								Collection.class.cast(object).add(nextObject);
							} else if (obj_i < length) {
								Collection.class.cast(object).add(tempCollection[obj_i]);
							} else {
								Collection.class.cast(object).add(null);
							}
						}
					}
				} else if (Map.class.isInstance(object)) {
					nextObject = Map.class.cast(object).get(property);
					if (nextObject == null) {
						nextObject = isNumberProperty(propertySplits[i + 1]) ? IcwUtils.listObj() : IcwUtils.mapObject();
						Map.class.cast(object).put(property, nextObject);
					}
				} else {
					PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(object.getClass(), property);
					try {
						nextObject = pd.getReadMethod().invoke(object);
						if (nextObject == null) {
							nextObject = pd.getReadMethod().getReturnType().getConstructor().newInstance();
							if (pd.getWriteMethod() != null) {
								pd.getWriteMethod().invoke(object, nextObject);
							}
						}
					} catch (Exception e) {
						object = null;
						break;
					}
				}
				parent = object;
				object = nextObject;
			}
			if (object == null) {
				return;
			} else if (Collection.class.isInstance(object)) {
				Object[] tempCollection = toObjectArray(object);
				String lastProperty = propertySplits[propertySplits.length - 1];
				int length = tempCollection.length;
				int index = isNumberAppendChar(lastProperty) ? length : Integer.valueOf(lastProperty, 10);
				int finalLength = Math.max(length, index + 1);
				Collection.class.cast(object).clear();
				for (int i = 0; i < finalLength; i++) {
					if (i == index) {
						Collection.class.cast(object).add(value);
					} else if (i < length) {
						Collection.class.cast(object).add(tempCollection[i]);
					} else {
						Collection.class.cast(object).add(null);
					}
				}
			} else if (object.getClass().isArray()) {
				Array.set(object, Integer.valueOf(propertySplits[propertySplits.length - 1], 10), value);
			} else if (Map.class.isInstance(object)) {
				Map.class.cast(object).put(propertySplits[propertySplits.length - 1], value);
			} else {
				PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(object.getClass(), propertySplits[propertySplits.length - 1]);
				try {
					pd.getWriteMethod().invoke(object, value);
				} catch (Exception e) {
				}
			}
		}
	}

	private static boolean isNumberProperty(String property) {
		return Pattern.matches(NUMBER_REGEX, property) || isNumberAppendChar(property);
	}

	private static boolean isNumberAppendChar(String property) {
		return property.indexOf(ConstSettings.NUMBER_APPEND_CHAR) == 0;
	}

	public static String[] splitProperties(String propertyPath) {
		return Stream.of(propertyPath.split("(\\]\\.)|(\\]\\[)|(\\[)|(\\])|(\\.)")).filter(s -> !StringUtils.equals("", s)).collect(Collectors.toList()).toArray(new String[0]);
	}

	public static boolean isBasicType(Class<?> clazz) {
		if (clazz.isPrimitive()) {
			return true;
		} else if (Number.class.isAssignableFrom(clazz)) {
			return true;
		} else if (String.class.isAssignableFrom(clazz)) {
			return true;
		} else if (Character.class.isAssignableFrom(clazz)) {
			return true;
		} else if (Boolean.class.isAssignableFrom(clazz)) {
			return true;
		} else if (Date.class.isAssignableFrom(clazz)) {
			return true;
		} else {
			return false;
		}
	}

	public static boolean isBasicType(Object obj) {
		if (obj == null) {
			return true;
		} else if (Class.class.isInstance(obj)) {
			return isBasicType(Class.class.cast(obj));
		} else {
			return isBasicType(obj.getClass());
		}
	}

	public static Object[] toObjectArray(Object srcObjs) {
		Object[] objs;
		if (srcObjs == null) {
			return new Object[] {};
		} else if (Collection.class.isInstance(srcObjs)) {
			if (isLazyInstance(srcObjs)) {
				objs = toObjectArray(loadLazyInstance(srcObjs));
			} else {
				objs = Collection.class.cast(srcObjs).toArray();
			}
		} else if (srcObjs.getClass().isArray()) {
			int length = Array.getLength(srcObjs);
			objs = (Object[]) Array.newInstance(Object.class, length);
			for (int i = 0; i < length; i++) Array.set(objs, i, Array.get(srcObjs, i));
		} else {
			objs = new Object[] { srcObjs };
		}
		return objs;
	}

	public static Object deepCopy(Object source) {
		return deepCopy(source, new DiyMap<Object, Object>());
	}

	private static Object deepCopy(Object source, DiyMap<Object, Object> map) {
		if (isBasicType(source) || isLazyInstance(source)) {
			return source;
		} else if (map.contains(source)) {
			return map.get(source);
		} else if (Collection.class.isInstance(source) || source.getClass().isArray()) {
			List<Object> list = IcwUtils.listObj();
			Object[] objects = toObjectArray(source);
			for (int objs_i = 0; objs_i < objects.length; objs_i++) {
				list.add(deepCopy(objects[objs_i], map));
			}
			return list;
		} else {
			Map<String, Object> obj = cacheAndReturn(map, source, IcwUtils.mapObject());
			for (String key : keyOf(source)) {
				obj.put(key, deepCopy(readValueByProperty(source, key), map));
			}
			return obj;
		}
	}

	private static <T> T cacheAndReturn(DiyMap<Object, Object> map, Object source, T target) {
		map.put(source, target);
		return target;
	}

	public static String[] keyOf(Object params) {
		if (Map.class.isInstance(params)) {
			return ArrayUtils.toStringArray(Map.class.cast(params).keySet().toArray());
		} else if (Collection.class.isInstance(params)) {
			int size = Collection.class.cast(params).size();
			String[] indexes = new String[size];
			for (int i = 0; i < indexes.length; i++) {
				indexes[i] = i + "";
			}
			return indexes;
		} else {
			List<String> methodProperties = Stream.of(BeanUtils.getPropertyDescriptors(params.getClass())).map(p -> p.getName()).filter(n -> !StringUtils.equals("class", n)).collect(Collectors.toList());
			List<String> fieldProperties = Stream.of(params.getClass().getFields())// e
					.filter(f -> Modifier.isPublic(f.getModifiers()))// e
					.filter(f -> !Modifier.isStatic(f.getModifiers()))// e
					.map(f -> f.getName()).collect(Collectors.toList());
			List<String> list = new LinkedList<>();
			list.addAll(methodProperties);
			list.addAll(fieldProperties);
			return list.toArray(ArrayUtils.toArray());
		}
	}

	@SuppressWarnings("unchecked")
	public static Object[] loadMany(Class<? extends BasePoEntity> entityClass, String id, String property) throws Exception {
		GenericSrvProxy srv = BeanCtxUtils.getBeanByType(GenericSrvProxy.class);
		List<?> res = srv.query(entityClass, new IcwCriteria(ArrayUtils.toArray(new IcwCriterion("eq", "id", id)), ArrayUtils.toArray(new IcwProjection("group", property + ".id"))), null);
		Object[] values = (Object[]) PoEntityHelper.readValueByProperty(res, "0.values");
		if (values == null || values.length == 0 || values[0] == null) {
			return ArrayUtils.toArray();
		} else {
			String[] ids = IcwUtils.getQueryListFirstFieldStrings(res);
			return srv.query((Class<? extends BasePoEntity>) findTypeByProperty(entityClass, property), new IcwCriteria(new IcwCriterion("in", "id", ids)), null).toArray();
		}
	}

	public static Class<?> findTypeByProperty(Class<?> rootClass, String propertyPath) {
		String[] properties = splitProperties(propertyPath);
		Class<?> pathClass = rootClass;
		for (int i = 0; i < properties.length; i++) {
			String property = properties[i];
			PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(pathClass, property);
			if (pd == null) {
				pathClass = null;
				break;
			} else if (Collection.class.isAssignableFrom(pd.getReadMethod().getReturnType())) {
				pathClass = findComponentType(pd);
			} else {
				pathClass = pd.getReadMethod().getReturnType();
			}
		}
		return pathClass;
	}

	@SuppressWarnings("unchecked")
	public static Object loadLazyInstance(Object object) {
		try {
			GenericSrvProxy srv = BeanCtxUtils.getBeanByType(GenericSrvProxy.class);
			if (PersistentCollection.class.isInstance(object)) {
				PersistentCollection o = PersistentCollection.class.cast(object);
				String declaringId = (String) readValueByProperty(o.getOwner(), "id");
				String property = o.getRole().substring(o.getRole().lastIndexOf(".") + 1, o.getRole().length());
				return loadMany((Class<? extends BasePoEntity>) o.getOwner().getClass(), declaringId, property);
			}
			if (HibernateProxy.class.isInstance(object)) {
				return srv.one((Class<? extends BasePoEntity>) object.getClass(), (String) readValueByProperty(object, "id"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static boolean isLazyInstance(Object object) {
		return false// e
				|| (HibernateProxy.class.isInstance(object) && HibernateProxy.class.cast(object).getHibernateLazyInitializer().isUninitialized())// e
				|| (PersistentCollection.class.isInstance(object) && !PersistentCollection.class.cast(object).wasInitialized());
	}

	public static Object[] transFormStructures(Object[] objs, String... srcProperties) throws Exception {
		Object[] res = new Object[objs.length];
		for (int i = 0; i < objs.length; i++) {
			res[i] = transFormStructure(objs[i], srcProperties);
		}
		return res;
	}

	@SuppressWarnings("unchecked")
	public static Object transFormStructure(Object obj, String... srcProperties) throws Exception {
		Object src = deepCopy(obj);
		Map<String, Object> res = new LinkedHashMap<>();
		for (String temp : srcProperties) {
			String[] splits = temp.split("=");
			String[] tgtPaths = splitPath(splits[0]);
			String[] srcPaths = splitPath(splits[1]);
			int arrayIndex = 0;
			int maxIndex = 0;
			while (arrayIndex <= maxIndex) {
				Object valueContainer = src;
				for (int i = 0; i < srcPaths.length - 1; i++) {
					String srcPath = srcPaths[i];
					if (srcPath.indexOf("[]") >= 0) {
						String realSrcPath = srcPath.substring(0, srcPath.length() - 2);
						Object[] valueContainers = toObjectArray(readValueByProperty(valueContainer, realSrcPath));
						if (valueContainer == null || keyOf(valueContainer).length == 0) {
							maxIndex = -1;
						} else {
							valueContainer = valueContainers[arrayIndex];
							maxIndex = valueContainers.length - 1;
						}
					} else {
						valueContainer = readValueByProperty(valueContainer, srcPath);
					}
				}
				Map<String, Object> container = res;
				for (int i = 0; i < tgtPaths.length - 1; i++) {
					String tgtPath = tgtPaths[i];
					if (tgtPath.indexOf("[]") >= 0) {
						String readTgtPath = tgtPath.substring(0, tgtPath.length() - 2);
						List<Map<String, Object>> containers = (List<Map<String, Object>>) container.get(readTgtPath);
						if (containers == null) {
							container.put(readTgtPath, containers = new LinkedList<>());
						}
						while (arrayIndex >= containers.size()) {
							containers.add(new LinkedHashMap<String, Object>());
						}
						container = (Map<String, Object>) containers.get(arrayIndex);
					} else {
						Map<String, Object> tempContainer = (Map<String, Object>) container.get(tgtPath);
						if (tempContainer == null) {
							container.put(tgtPath, tempContainer = new LinkedHashMap<>());
						}
						container = tempContainer;
					}
				}
				if (valueContainer == null) {
					container.put(tgtPaths[tgtPaths.length - 1], null);
				} else {
					container.put(tgtPaths[tgtPaths.length - 1], readValueByProperty(valueContainer, srcPaths[srcPaths.length - 1]));
				}
				arrayIndex++;
			}
		}
		if (res.containsKey("")) {
			Map<String, Object> oriRes = res;
			Object newRes = oriRes.get("");
			oriRes.remove("");
			for (Entry<String, Object> entry : oriRes.entrySet()) {
				writeValueByProperty(newRes, entry.getKey(), entry.getValue());
			}
			return newRes;
		} else {
			return res;
		}
	}

	private static String[] splitPath(String path) {
		path = path.replaceAll("\\[\\]", ARRY_RPLC_MARK);
		return Stream.of(splitProperties(path)).map(s -> s.replaceAll(ARRY_RPLC_MARK, "[]")).collect(Collectors.toList()).toArray(ArrayUtils.toArray());
	}

	// propertyPath means value from entity
	// ${propertyPath} means value from context
	public static Map<String, Object> transFromRoot(Map<String, Object> context, BasePoEntity entity, String... mappings) throws Exception {
		IcwCriteria cri = new IcwCriteria();
		List<IcwCriterion> crionList = new LinkedList<>();
		crionList.add(new IcwCriterion("eq", "id", entity.getId()));
		List<IcwProjection> projList = new LinkedList<>();
		String[] propertyLefts = new String[mappings.length];
		String[] propertyRights = new String[mappings.length];
		for (int i = 0; i < mappings.length; i++) {
			String[] splits = mappings[i].split("=");
			propertyLefts[i] = splits[0].trim();
			propertyRights[i] = splits[1].trim();
			if (propertyRights[i].indexOf("${") != 0) {
				crionList.add(new IcwCriterion("ne", propertyRights[i].replaceAll("\\[\\]", ""), null));
				projList.add(new IcwProjection(null, propertyRights[i].replaceAll("\\[\\]", "")));
			}
		}
		cri.criterions = crionList.toArray(ArrayUtils.toArray());
		cri.projections = projList.toArray(ArrayUtils.toArray());
		GenericSrvProxy srv = BeanCtxUtils.getBeanByType(GenericSrvProxy.class);
		Object[] res = toObjectArray(srv.query(entity.getClass(), cri, null));
		Map<String, Object> result = IcwUtils.mapObject();
		for (int i = 0; i < res.length; i++) {
			Object[] values = (Object[]) readValueByProperty(res[i], "values");
			int ctxVarCount = 0;
			for (int propertyIndex = 0; propertyIndex < mappings.length; propertyIndex++) {
				String propertyLeft = propertyLefts[propertyIndex];
				String propertyRight = propertyRights[propertyIndex];
				Object written;
				if (propertyRight.indexOf("${") == 0) {
					written = readValueByProperty(context, propertyRight.substring(2, propertyRight.length() - 1));
					ctxVarCount++;
				} else {
					written = values[propertyIndex - ctxVarCount];
				}
				writeValueByProperty(result, propertyLeft.replace("[]", "[" + i + "]"), written);
			}
		}
		return result;
	}

	public static Object parseJSEL(Object context, String initialValue) throws Exception {
		Object res = IcwUtils.tryCatch(() -> de().parse(initialValue, null));
		parseExpression(context, res);
		return res;
	}

	public static void parseExpression(Object context, Object res) throws Exception {
		if (PoEntityHelper.isBasicType(res)) {
			// skip
		} else if (Collection.class.isInstance(res) || res.getClass().isArray()) {
			int length = Array.getLength(PoEntityHelper.toObjectArray(res));
			for (int i = 0; i < length; i++) {
				paseExpression(context, res, i + "");
			}
		} else {
			String[] keys = PoEntityHelper.keyOf(res);
			for (int i = 0; i < keys.length; i++) {
				paseExpression(context, res, keys[i]);
			}
		}
	};

	private static void paseExpression(Object context, Object res, String key) throws Exception {
		Object value = PoEntityHelper.readValueByProperty(res, key);
		if (value == null) {
			// skip
		} else if (String.class.isInstance(value) && Pattern.matches("\\{\\{.+\\}\\}", String.class.cast(value))) {
			String valueStr = String.class.cast(value);
			String exp = valueStr.substring(2, valueStr.length() - 2);
			Object val;
			if (exp.length() == 0) {
				val = PoEntityHelper.readValueByProperty(context, exp);
			} else if ("{".equals(exp.substring(0, 1))) {
				val = de().parseMap(exp);
				parseExpression(context, val);
			} else if ("[".equals(exp.substring(0, 1))) {
				val = de().parseCollection(exp);
				parseExpression(context, val);
			} else {
				val = PoEntityHelper.readValueByProperty(context, exp);
			}
			PoEntityHelper.writeValueByProperty(res, key, val);
		} else if (Collection.class.isInstance(value) || value.getClass().isArray()) {
			int length = Array.getLength(PoEntityHelper.toObjectArray(value));
			for (int i = 0; i < length; i++) {
				paseExpression(context, value, i + "");
			}
		} else if (!PoEntityHelper.isBasicType(value)) {
			String[] keys = PoEntityHelper.keyOf(value);
			for (int i = 0; i < keys.length; i++) {
				paseExpression(context, value, keys[i]);
			}
		}
	}

	public static String[] loadProperty(Class<? extends BasePoEntity> entityClass, String entityId, String property, IcwCriteria cri) throws Exception {
		if (cri == null) cri = new IcwCriteria();
		if (StringUtils.isNotEmpty(entityId)) cri.criterions = ArrayUtils.addAll(ArrayUtils.toArray(new IcwCriterion("eq", "id", entityId)), cri.criterions);
		cri.projections = ArrayUtils.toArray(new IcwProjection("group", property));
		List<?> list = srv().query(entityClass, cri, null);
		return IcwUtils.getQueryListFirstFieldStrings(list.stream().filter(l -> l != null).collect(Collectors.toList()));
	}

	public static Set<String> getUsefulProperties(Class<? extends BasePoEntity> clazz) {
		Class<?> clasz = BasePoEntity.class;
		Set<String> ignSet = Stream.of(BeanUtils.getPropertyDescriptors(clasz)).map(pd -> pd.getName()).collect(Collectors.toSet());
		Set<String> allSet = Stream.of(BeanUtils.getPropertyDescriptors(clazz)).map(pd -> pd.getName()).collect(Collectors.toSet());
		Set<String> usefulSet = SetUtils.difference(allSet, ignSet);
		return new LinkedHashSet<>(usefulSet);
	}

	private static GenericSrvProxy srv() {
		return BeanCtxUtils.getBeanByType(GenericSrvProxy.class);
	}

	private static DataExchange de() {
		return BeanCtxUtils.getBeanByType(DataExchange.class);
	}

	public static String appendIfAbsent(String str, String ap) {
		int index = str.lastIndexOf(ap);
		if (index < 0 || str.length() != ap.length() + index) {
			return str + ap;
		} else {
			return str;
		}
	}

	public static <T> T objectAssign(T original, Object patches) throws Exception {
		String[] containKeys = keyOf(patches);
		if (!original.getClass().isInstance(patches)) {
			patches = de().parse(patches, original.getClass());
		}
		Set<String> allKeys = Stream.of(BeanUtils.getPropertyDescriptors(original.getClass())).map(p -> p.getName()).collect(Collectors.toSet());
		String[] ignoreKeys = SetUtils.difference(allKeys, SetUtils.unmodifiableSet(containKeys)).toArray(ArrayUtils.toArray());
		BeanUtils.copyProperties(patches, original, ignoreKeys);
		return original;
	}

	@SuppressWarnings("unchecked")
	public static Set<Class<PropertySettings<?>>> getPropertySettings() throws Exception {
		return ClassUtil.getClasses(ConstSettings.RESOURCE_PROPERTIES_PACKAGE).stream()// e
				.filter(c -> PropertySettings.class.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers()))// e
				.map(c -> (Class<PropertySettings<?>>) c)// e
				.collect(Collectors.toSet());
	}

	private static Map<String, Set<String>> publicActionMap = null;

	public static Map<String, Set<String>> getResourcePublicActionMap() throws Exception {
		if (publicActionMap == null) {
			synchronized ("getResourcePublicActionMap") {
				if (publicActionMap == null) {
					publicActionMap = new LinkedHashMap<>();
					Set<Class<PropertySettings<?>>> pses = getPropertySettings();
					for (Class<PropertySettings<?>> ps : pses) {
						PropertySettings<?> pi = ps.getConstructor().newInstance();
						Set<String> paSet = SetUtils.unmodifiableSet(IcwUtils.ifNull(pi.publicActions(), () -> new String[] {}));
						publicActionMap.put(IcwUtils.toFirstLowerCase(pi.getEntityClass().getSimpleName()), paSet);
					}
				}
			}
		}
		return publicActionMap;
	}

	public static void limitsParams(IcwPerm icwPerm, Map<String, Object> params, Map<String, Object> context) throws Exception {
		if (!StringUtils.isEmpty(icwPerm.getParamsLimits())) {
			context.put("params", params);
			Object[] actions = PoEntityHelper.toObjectArray(de().parseCollection(icwPerm.getParamsLimits()));
			rpc().process(actions, context);
			for (int i = 0; i < actions.length; i++) {
				Object action = actions[i];
				String k = (String) PoEntityHelper.readValueByProperty(action, "k");
				if (StringUtils.equals(k.substring(0, 1), "!")) {
					k = k.substring(1);
					PoEntityHelper.removeValueByProperty(params, k);
				} else {
					boolean isForce = true;
					if (StringUtils.equals(k.substring(0, 1), "@")) {
						isForce = false;
						k = k.substring(1);
					}
					if (isForce || !PoEntityHelper.containsKeyByProperty(params, k)) {
						PoEntityHelper.writeValueByProperty(params, k, PoEntityHelper.readValueByProperty(action, "v"));
					}
				}
			}
		}
	}

	public static void removeValueByProperty(Map<String, Object> params, String propertyPath) {
		String[] properties = splitProperties(propertyPath);
		if (properties.length == 0) {
			// skip
		} else {
			String containerPath = StringUtils.join(ArrayUtils.subarray(properties, 0, properties.length - 1), ".");
			Object container = PoEntityHelper.readValueByProperty(params, containerPath);
			if (container == null) {
				// skip
			} else if (Map.class.isInstance(container)) {
				Map.class.cast(container).remove(properties[properties.length - 1]);
			} else {
				PoEntityHelper.writeValueByProperty(container, properties[properties.length - 1], null);
			}
		}
	}

	private static ThreadLocal<Map<String, Object>> CTX_THREAD_LOCAL = new ThreadLocal<>();

	public static Map<String, Object> apiContext() {
		return IcwUtils.ifNull(CTX_THREAD_LOCAL.get(), () -> MapUtils.unmodifiableMap(IcwUtils.mapObject("user", IcwUtils.getUser(), "host", requestHost())));
	}

	public static void apiContext(Map<String, Object> ctx, MyRunnable run) throws Exception {
		try {
			CTX_THREAD_LOCAL.set(ctx);
			run.run();
		} finally {
			CTX_THREAD_LOCAL.remove();
		}
	}

	public static String requestHost() {
		try {
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			return request.getHeader("Origin");
		} catch (Exception e) {
			return null;
		}
	}

	public static IcwPerm findPermByRoute(String route) throws Exception {
		List<IcwPerm> perms;
		if (IcwUtils.getUser() != null) {
			perms = srv().query(IcwPerm.class, new IcwCriteria(// e
					"eq", "roles.users.id", IcwUtils.getUser().getId(), // e
					"eq", "route", route, // e
					"ne", "roles.name", ConstSettings.NAME_OF_DEFAULT_ROLE// e
			), null);
		} else {
			perms = new LinkedList<>();
		}
		if (perms.size() == 0) {
			perms = srv().query(IcwPerm.class, new IcwCriteria(// e
					"eq", "route", route, // e
					"eq", "roles.name", ConstSettings.NAME_OF_DEFAULT_ROLE// e
			), null);
		}
		if (perms.size() > 1) {
			throw ConstSettings.DUPLICATE_PERMISSION_DEFINITION;
		} else if (perms.size() == 1) {
			return perms.get(0);
		} else {
			return null;
		}
	}

	public static void fillParams(Class<?> clazz, Object params) throws Exception {
		for (String key : PoEntityHelper.keyOf(params)) {
			PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, key);
			if (pd != null) {
				Class<?> fieldType = pd.getReadMethod().getReturnType();
				Object value = PoEntityHelper.readValueByProperty(params, key);
				if (true// e
						&& value != null // e
						&& !PoEntityHelper.isBasicType(fieldType)// e
						&& PoEntityHelper.isCascadable(clazz, key)// e
				) {
					if (Collection.class.isAssignableFrom(fieldType)) {
						if (value.getClass().isArray() || Collection.class.isInstance(value)) {
							Object[] objects = PoEntityHelper.toObjectArray(value);
							for (int i = 0; i < objects.length; i++) {
								if (objects[i] != null) {
									fillParam(pd, objects[i]);
								}
							}
						}
					} else {
						fillParam(pd, value);
					}
				}
			}
		}
	}

	public static void fillParam(PropertyDescriptor pd, Object value) throws Exception {
		if (!PoEntityHelper.containsKeyByProperty(value, "__paramsFilled") && isChangable(pd.getReadMethod().getDeclaringClass(), pd.getName(), value)) {
			Class<?> fieldType = findTypeByProperty(pd.getReadMethod().getDeclaringClass(), pd.getName());
			String resourceName = IcwUtils.toFirstLowerCase(fieldType.getSimpleName());
			String action = !PoEntityHelper.containsKeyByProperty(value, "id") ? "post" : "patch";
			String route = concatRoute(resourceName, action);
			IcwPerm perm = findPermByRoute(route);
			if (perm != null) {
				Map<String, Object> valueObj = IcwUtils.castMap(value);
				Map<String, Object> ctx = new LinkedHashMap<>(PoEntityHelper.apiContext());
				PoEntityHelper.limitsParams(perm, valueObj, ctx);
			}
			PoEntityHelper.writeValueByProperty(value, "__paramsFilled", true);
			fillParams(fieldType, value);
		}
	}

	public static boolean isChangable(Class<?> entityClass, String fieldName, Object value) {
		boolean hasId = PoEntityHelper.readValueByProperty(value, "id") != null;
		boolean able = hasId// e
				? PoEntityHelper.isUpdatable(entityClass, fieldName)// e
				: PoEntityHelper.isInsertable(entityClass, fieldName);
		return able;
	}

	public static String concatRoute(String resourceName, String action) {
		return String.format("/resource/%s/%s", resourceName, action);
	}

	private static ResourceContextProcessor rpc() {
		return BeanCtxUtils.getBeanByType(ResourceContextProcessor.class);
	}

	private static PropertiesData PROPERTIES_DATA = null;

	public static PropertiesData getPropertiesData() throws Exception {
		if (PROPERTIES_DATA == null) {
			synchronized (PropertiesData.class) {
				if (PROPERTIES_DATA == null) {
					PropertiesData propertiesData = new PropertiesData();
					initializeDescriptions(propertiesData);
					initializeProperties(propertiesData);
					PROPERTIES_DATA = propertiesData;
				}
			}
		}
		return PROPERTIES_DATA;
	}

	@SuppressWarnings("unchecked")
	private static void initializeProperties(PropertiesData propertiesData) throws Exception {
		Map<String, Map<String, PropertyDescription>> descriptionMap = propertiesData.descriptionMap;
		Map<String, Map<String, String[]>> propertiesMap = propertiesData.propertiesMap = new HashMap<>();
		Map<String, Map<String, PropertyGroup>> propertyGroupsMap = propertiesData.propertyGroupsMap = new HashMap<>();
		Set<Class<PropertySettings<?>>> psClasses = PoEntityHelper.getPropertySettings();
		Set<Class<?>> entityClasses = new HashSet<>(PoEntityHelper.getTreeSetForClass());
		for (Class<PropertySettings<?>> temp : psClasses) {
			PropertySettings<?> ps = temp.newInstance();
			Map<String, String[]> type_prop_map;
			propertiesMap.put(ps.getOrefType(), type_prop_map = new HashMap<>());
			Map<String, PropertyGroup> type_group_map;
			propertyGroupsMap.put(ps.getOrefType(), type_group_map = new HashMap<>());
			Method[] methods = temp.getMethods();
			for (Method method : methods) {
				if (Pattern.matches("\\w+Properties$", method.getName())) {
					String typeName = method.getName();
					typeName = typeName.substring(0, typeName.length() - "Properties".length());
					type_prop_map.put(typeName, (String[]) method.invoke(ps));
				}
				if (Pattern.matches("^\\w+Labels$", method.getName())) {
					String typeName = method.getName();
					typeName = typeName.substring(0, typeName.length() - "Labels".length());
					String indexesMethodName = method.getName();
					indexesMethodName = indexesMethodName.substring(0, indexesMethodName.length() - "Labels".length()) + "Indexes";
					Method indexesMethod = temp.getMethod(indexesMethodName);
					String[] labels = (String[]) method.invoke(ps);
					Integer[] indexes = (Integer[]) indexesMethod.invoke(ps);
					type_group_map.put(typeName, new PropertyGroup(labels, indexes, temp.getSimpleName()));
				}
			}
			Map<String, PoVali> validationMap = ps.nameValiSettings();
			Map<String, PropertyDescription> orefDescriptionMap = descriptionMap.get(ps.getOrefType());
			for (Entry<String, PoVali> valiEntry : validationMap.entrySet()) {
				orefDescriptionMap.get(valiEntry.getKey()).getMode().setValidations(valiEntry.getValue().build());
			}
			entityClasses.remove(ps.getEntityClass());
		}
		// Dynamic add validation
		for (Class<?> temp : entityClasses) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(PropertySettings.class);
			enhancer.setCallback(NoOp.INSTANCE);
			@SuppressWarnings("rawtypes")
			PropertySettings o = PropertySettings.class.cast(enhancer.create());
			o.bindEntityClass(temp);
			o.nameValiSettings(o.nameValiSettings());
			Map<String, PoVali> validationMap = o.nameValiSettings();
			Map<String, PropertyDescription> orefDescriptionMap = descriptionMap.get(o.getOrefType());
			for (Entry<String, PoVali> valiEntry : validationMap.entrySet()) {
				orefDescriptionMap.get(valiEntry.getKey()).getMode().setValidations(valiEntry.getValue().build());
			}
		}
	}

	private static void initializeDescriptions(PropertiesData propertiesData) throws Exception {
		Map<String, Map<String, PropertyDescription>> descriptionsMap = propertiesData.descriptionMap = new LinkedHashMap<>();
		Set<Class<?>> entityClasses = PoEntityHelper.getTreeSetForClass();
		for (Class<?> temp : entityClasses) {
			Map<String, PropertyDescription> description_map = new LinkedHashMap<>();
			for (PropertyDescriptor temptemp : Stream.of(BeanUtils.getPropertyDescriptors(temp)).filter(p -> PoEntityHelper.filterPd(p)).collect(Collectors.toSet())) {
				PropertyDescription p = basicType(temptemp, temp);
				putDescription(description_map, p);
			}
			descriptionsMap.put(temp.getSimpleName(), description_map);
		}
	}

	private static void putDescription(Map<String, PropertyDescription> description_map, PropertyDescription f) throws Exception {
		description_map.put(f.getName(), f);
		// add confirm/original/new password description if it's "pswd"
		if (f.getType().equals("pswd")) {
			putAnotherFieldWithPrifix(description_map, f, "confirm");
			{
				PoMode confirmMode = description_map.get("confirmPassword").getMode();
				confirmMode.setValidations(ArrayUtils.add(confirmMode.getValidations(), "confirmPswd"));
			}
			putAnotherFieldWithPrifix(description_map, f, "new");
			putAnotherFieldWithPrifix(description_map, f, "original");
		}
	}

	private static void putAnotherFieldWithPrifix(Map<String, PropertyDescription> description_map, PropertyDescription f, String prefix) throws Exception {
		PropertyDescription another = de().parse(PoEntityHelper.deepCopy(f), PropertyDescription.class);
		another.setName(prefix + another.getName().substring(0, 1).toUpperCase() + another.getName().substring(1));
		another.setLabel(PoEntityHelper.toLabel(another));
		another.setPlaceholder(another.getLabel());
		description_map.put(another.getName(), another);
	}

	private static PropertyDescription basicType(PropertyDescriptor pd, Class<?> entityClass) throws Exception {
		PropertyDescription pod = new PropertyDescription();
		{ // initializing basic fields
			pod.setDeclaringType(entityClass.getSimpleName());
			pod.setName(pd.getName());
			pod.setLabel(PoEntityHelper.toLabel(pod));
			pod.setPlaceholder(pod.getLabel());
		}
		return basicType(pod, pd, pd.getPropertyType());
	}

	// "bool" | "date" | "dict" | "oref" | "file" | "nmbr" | "pswd" | "txea" |
	// "text"
	private static PropertyDescription basicType(PropertyDescription pod, PropertyDescriptor pd, Class<?> fieldClazz) throws Exception {
		if (pd.getPropertyType().isArray() || Collection.class.isAssignableFrom(pd.getPropertyType())) {
			pod.setIzArray(true);
			pod.setSingularLabel(Inflector.getInstance().singularize(pod.getLabel()));
		}
		//
		/*-*/if (PoEntityHelper.isMult(fieldClazz)) basicType(pod, pd, PoEntityHelper.findComponentType(pd));
		else if (PoEntityHelper.isBool(fieldClazz)) pod.setType("bool");
		else if (PoEntityHelper.isNmbr(fieldClazz)) pod.setType("nmbr");
		else if (PoEntityHelper.isPswd(pd, fieldClazz)) pod.setType("pswd");
		else if (PoEntityHelper.isTxea(pd, fieldClazz)) pod.setType("txea");
		else if (PoEntityHelper.isDict(pd, fieldClazz)) pod.setDictGroupName(PoEntityHelper.findDGN(pd, fieldClazz));
		else if (PoEntityHelper.isText(fieldClazz)) pod.setType("text");
		else if (PoEntityHelper.isDate(fieldClazz)) pod.setType("date");
		else if (PoEntityHelper.isOref(fieldClazz)) pod.setOrefType(fieldClazz.getSimpleName());
		return pod;
	}
}
